package org.sdp.pearproject.dao.domain;

import com.alibaba.fastjson.JSONObject;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

public class BaseExample {
    protected static int appendCriteriaOrderByItem(StringBuilder sb, String name, String value) throws Exception {
        if (value == null) {
            return 0;
        }
        if (sb.length() > 0) {
            sb.append(",");
        }
        value = value.trim();
        if ("desc/gbk".equals(value)) {
            sb.append("convert(");
            sb.append(name);
            sb.append(" using gbk) desc");
        } else if ("asc/gbk".equals(value)) {
            sb.append("convert(");
            sb.append(name);
            sb.append(" using gbk) asc");
        } else if ("desc".equals(value)) {
            sb.append(name);
            sb.append(" desc");
        } else if ("asc".equals(value) || "".equals(value)) {
            sb.append(name);
            sb.append(" asc");
        } else {
            throw new Exception("非法的order by类型:"+value);
        }
        return 1;
    }

    //           class     op         field   method
    protected static Map<Class, Map<String, Map<Field, Method>>> clzMap = new HashMap<>();
    protected static Map<Class, Map<String, Map<Field, Method>>> clzPropMap = new HashMap<>();

    protected static Map<Class, Map<String, Method>> examplePropSetMap = new HashMap<>();
    public void fixExample(Class objClz, Class stringClz, Object criteria, JSONObject options) throws Exception {
        if (options == null) {
            return;
        }

        synchronized (examplePropSetMap) {
            Class clz = this.getClass();

            Map<String, Method> fieldMap = examplePropSetMap.get(clz);
            if (CollectionUtils.isEmpty(fieldMap)) {
                fieldMap = new HashMap<>();
                examplePropSetMap.put(clz, fieldMap);

                Map<String, Method> methodMap = new HashMap<>();
                {
                    Method[] methods = clz.getDeclaredMethods();
                    for(Method method: methods) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            continue;
                        }
                        String methodName = method.getName().toLowerCase();
                        if (!methodName.startsWith("set")) {
                            continue;
                        }
                        if (methodMap.containsKey(methodName)) {
                            throw new Exception("方法名重名："+this.getClass().getName()+","+methodName);
                        }
                        methodMap.put(methodName, method);
                    }

                    Field[] fields = clz.getDeclaredFields();
                    for(Field field: fields) {
                        if (Modifier.isFinal(field.getModifiers())) {
                            continue;
                        }
                        String fieldName= field.getName().toLowerCase();
                        Method method = methodMap.get("set"+fieldName);
                        if (method == null) {
                            continue;
                        }
                        field.setAccessible(true);
                        method.setAccessible(true);
                        fieldMap.put(fieldName, method);
                    }
                }
            }
        }

        Object intV = options.getInteger("limit");
        if (intV != null) {
            synchronized (examplePropSetMap.get(this.getClass())) {
                Method method = examplePropSetMap.get(this.getClass()).get("limitclause");
                if (method != null) {
                    method.invoke(this, intV);
                } else {
                    throw new Exception("未定义方法：setLimitClause");
                }

            }
        }
        intV = options.get("count");
        if (intV != null) {
            synchronized (examplePropSetMap.get(this.getClass())) {
                Method method = examplePropSetMap.get(this.getClass()).get("countclause");
                if (method != null) {
                    method.invoke(this, intV);
                } else {
                    throw new Exception("未定义方法：setCountClause");
                }
            }
        }
        intV = options.get("top");
        if (intV != null) {
            synchronized (examplePropSetMap.get(this.getClass())) {
                Method method = examplePropSetMap.get(this.getClass()).get("topclause");
                if (method != null) {
                    method.invoke(this, intV);
                } else {
                    throw new Exception("未定义方法：setTop");
                }
            }
        }
        JSONObject jsonV = options.getJSONObject("setnull");
        if(jsonV != null) {
            Object obj = JSONObject.toJavaObject(jsonV, stringClz);
            synchronized (examplePropSetMap.get(this.getClass())) {
                Method method = examplePropSetMap.get(this.getClass()).get("setnull");
                if (method != null) {
                    method.invoke(this, obj);
                } else {
                    throw new Exception("未定义方法：setSetNull");
                }
            }
        }

        if(options.containsKey("=")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("="), objClz);
            fixExample("EqualTo", 1, obj, criteria);
        }
        if(options.containsKey(">")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject(">"), objClz);
            fixExample("GreaterThan", 1, obj, criteria);
        }
        if(options.containsKey(">=")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject(">="), objClz);
            fixExample("GreaterThanOrEqualTo", 1, obj, criteria);
        }
        if(options.containsKey("!null")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("!null"), stringClz);
            fixExample("IsNotNull", 0, obj, criteria);
        }
        if(options.containsKey("null")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("null"), stringClz);
            fixExample("IsNull", 0, obj, criteria);
        }
        if(options.containsKey("<")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("<"), objClz);
            fixExample("LessThan", 1, obj, criteria);
        }
        if(options.containsKey("<=")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("<="), objClz);
            fixExample("LessThanOrEqualTo", 1, obj, criteria);
        }
        if(options.containsKey("like")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("like"), objClz);
            fixExample("Like", 1, obj, criteria);
        }
        if(options.containsKey("!=")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("!="), objClz);
            fixExample("NotEqualTo", 1, obj, criteria);
        }
        if(options.containsKey("!like")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("!like"), objClz);
            fixExample("NotLike", 1, obj, criteria);
        }
        if(options.containsKey("regexp")) {
            Object obj = JSONObject.toJavaObject(options.getJSONObject("regexp"), objClz);
            fixExample("Regexp", 1, obj, criteria);
        }
    }
    public void fixExample(String op, Integer paramCount, Object obj, Object criteria) throws Exception {
        Map<String, Map<Field, Method>> opMap;
        Map<String, Map<Field, Method>> propMap;
        synchronized (clzMap) {
            if (!clzMap.containsKey(this.getClass())) {
                clzMap.put(this.getClass(), new HashMap<>());
                clzPropMap.put(this.getClass(), new HashMap<>());
            }
            opMap = clzMap.get(this.getClass());
            propMap = clzPropMap.get(this.getClass());
        }
        synchronized (opMap) {
            Map<Field, Method> fieldMap = propMap.get(op);
            if (fieldMap == null) {
                fieldMap = new HashMap<>();
                propMap.put(op, fieldMap);

                Class clz = obj.getClass();
                Map<String, Method> methodMap = new HashMap<>();
                while(true) {
                    Method[] methods = clz.getDeclaredMethods();
                    for(Method method: methods) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            continue;
                        }
                        String methodName = method.getName().toLowerCase();
                        if (!methodName.startsWith("get")) {
                            continue;
                        }
                        if (methodMap.containsKey(methodName)) {
                            throw new Exception("方法名重名："+this.getClass().getName()+","+methodName);
                        }
                        methodMap.put(methodName, method);
                    }
                    if (!clz.getSimpleName().endsWith("WithBLOBs")) {
                        break;
                    }
                    clz = clz.getSuperclass();
                }
                clz = obj.getClass();
                while(true) {
                    Field[] fields = clz.getDeclaredFields();
                    for(Field field: fields) {
                        if (Modifier.isFinal(field.getModifiers())) {
                            continue;
                        }
                        Method method = methodMap.get("get"+field.getName().toLowerCase());
                        if (method == null) {
                            throw new Exception("未找到属性对应get方法："+this.getClass().getName()+","+field.getName());
                        }
                        field.setAccessible(true);
                        method.setAccessible(true);
                        fieldMap.put(field, method);
                    }
                    if (!clz.getSimpleName().endsWith("WithBLOBs")) {
                        break;
                    }
                    clz = clz.getSuperclass();
                }
            }

            Map<Field, Method> fieldForAndMap = opMap.get(op);
            if (fieldForAndMap == null) {
                fieldForAndMap = new HashMap<>();
                opMap.put(op, fieldForAndMap);

                Class criteriaClass = criteria.getClass();
                while(criteriaClass != null && !"GeneratedCriteria".equals(criteriaClass.getSimpleName())) {
                    criteriaClass = criteriaClass.getSuperclass();
                }
                if (criteriaClass == null) {
                    throw new Exception("未继承自GeneratedCriteria:"+this.getClass().getName());
                }
                Method[] methods = criteriaClass.getDeclaredMethods();
                Map<String, Method> methodMap = new HashMap<>();
                for(Method method: methods) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        continue;
                    }
                    String methodName = method.getName().toLowerCase();
                    if (!methodName.startsWith("and")) {
                        continue;
                    }
                    if (methodMap.containsKey(methodName)) {
                        throw new Exception("方法名重名："+this.getClass().getName()+","+methodName);
                    }
                    methodMap.put(methodName, method);
                }
                for(Field field: fieldMap.keySet()) {
                    Method method = methodMap.get("and"+field.getName().toLowerCase()+op.toLowerCase());
                    if (method != null) {
                        method.setAccessible(true);
                        fieldForAndMap.put(field, method);
                    }
                }
            }
            for(Field field: fieldMap.keySet()) {
                Method method = fieldForAndMap.get(field);
                if (method != null) {
                    Object v = field.get(obj);
                    if (v != null) {
                        if (paramCount == 0) {
                            method.invoke(criteria);
                        } else if (paramCount == 1) {
                            method.invoke(criteria, v);
                        }
                    }
                }
            }
        }
    }

}
